package com.note.config.log;

import com.note.common.domain.Log;
import com.note.common.enums.LogEnum;
import com.note.common.interfaces.SystemLog;
import com.note.common.utils.JwtUtil;
import com.note.common.utils.RequestUtil;
import com.note.common.utils.TimeUtils;
import com.note.config.service.MqServiceImpl;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

/**
 * @Package：com.note.config.constants.log
 * @Name：LogAspeect
 * @Author：热伊木
 * @Email：uyevan@163.com
 * @Date：2024-05-01-8:49
 * @Description：日记拦截并发送MQ消息---AOP切面
 */
@Slf4j
@Order(1)
@Aspect
@Component
public class LogAspeect {

    @Autowired
    private MqServiceImpl mqService;

    //TODO 注入切点
    @Pointcut("@annotation(com.note.common.interfaces.SystemLog)") //此注解为切点入口
    public void logPointcut() {
    }

    //TODO 前置通知  [方法前执行]
    //@Before("logPointcut()")
    public void beforeLog(JoinPoint joinPoint) {
        log.info("前置通知出发:{}", joinPoint);
    }

    //TODO 环绕通知  [方法前/后执行]
    @Around("logPointcut()")
    public Object aroundLog(ProceedingJoinPoint pjp) throws Throwable {
        //调用目标方法
        Object result = pjp.proceed();
        Object args = pjp.getArgs();
        //封装正常日志
        Log dLog = wrapSystemLog(pjp, result);
        log.info("环绕通知:{}", dLog);
        //发送MQ消息
        mqService.sendMessage(dLog);
        return result;
    }

    //TODO 后置返回通知 --- 只有正常结束时才执行 [方法后执行]
    //@AfterReturning(value = "logPointcut()", returning = "ret")
    public void afterReturnLog(Object ret) {
        log.info("后置增强通知出发:{}", ret);
    }

    //TODO 后置通知  [方法后执行]
    //@After("logPointcut()")
    public void afterLog(JoinPoint joinPoint) {
        //封装正常日志
        Log dLog = wrapSystemLog(joinPoint, null);
        log.info("dLog:{}", dLog);

        //发送MQ消息
        mqService.sendMessage(dLog);

    }

    //TODO 异常通知  [方法后执行]
    //@AfterThrowing(value = "logPointcut()", throwing = "throwable")
    public void throwLog(JoinPoint joinPoint, Throwable throwable) {
        //封装异常日志
        Log eLog = wrapSystemLog(joinPoint, throwable.getMessage());
        eLog.setLogType(LogEnum.THROW_LOG.getType());
        eLog.setLogMessage(String.format("%s-----err-----%s", eLog.getLogMessage(), eLog));
        log.info("eLog:{}", eLog);

        //发送MQ消息
        mqService.sendMessage(eLog);
    }


    /**
     * 封装日志
     *
     * @param joinPoint
     * @return
     */
    private Log wrapSystemLog(JoinPoint joinPoint, Object object) {
        //获取请求响应对象
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        HttpServletResponse response = attributes.getResponse();
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Log dLog = new Log();
        //获取方法全路径
        String methodName = String.format("%s.%s", signature.getDeclaringTypeName(), signature.getName());
        //获取注解参数值 [日志类型]
        SystemLog systemLog = signature.getMethod().getAnnotation(SystemLog.class);
        //获取用户信息 [Token取值]
        String token = request.getHeader(JwtUtil.AUTHORIZATION);
        if (StringUtils.hasLength(token)) {
            //获取Token
            token = token.replace(JwtUtil.TOKEN_PREFIX, "");
            String username = JwtUtil.getPayloadFromToken(token, "username");
            //TODO 1.optName
            dLog.setOptName(username);
        }
        //日记类型
        if (StringUtils.hasLength(systemLog.type().getType())) {
            //TODO 2.logType
            dLog.setLogType(systemLog.type().getType());
        }
        //TODO 3.logMessage
        dLog.setLogMessage(methodName);
        //TODO 4.logCreateTime
        dLog.setCreateTime(TimeUtils.getCurrentDate());
        //TODO 5.ipAddress
        dLog.setIpAddress(RequestUtil.getRequestIp(request));
        //TODO 6.Method
        dLog.setRequestMethod(request.getMethod());
        //TODO 7.URL
        dLog.setRequestUrl(request.getRequestURI());
        //TODO 8.responseCode
        dLog.setResponseCode(response.getStatus());
        //TODO 9.responseMessage --- 消息太长暂不存储
        /*String fullString = object.toString();
        dLog.setResponseMessage(fullString.length() > 300 ? fullString.substring(0, 300) : fullString);*/
        //TODO 10.additionalData --- 预留
        /*dLog.setAdditionalData("");*/
        return dLog;
    }
}
